#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Modbus RS232服务器示例
基于pymodbus 3.11.2实现，支持单从站设备模拟
"""

import random
import time
import logging
import threading
from datetime import datetime

from pymodbus.server import StartSerialServer
from pymodbus.datastore import ModbusServerContext, ModbusDeviceContext
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus import ModbusDeviceIdentification, FramerType

# 日志配置
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# --------------------------
# 参数配置
# --------------------------
PORT = '/dev/tty.usbserial-1121'  # RS232串口端口
BAUDRATE = 9600                    # 波特率
NUM_SLAVES = 1                     # RS232通常是点对点通信，单个从站
REGISTER_COUNT = 6                 # 每个从站的寄存器数量（模拟传感器数据）
UPDATE_INTERVAL = 2                # 寄存器更新间隔（秒）

# --------------------------
# 创建单个从站的数据存储区
# --------------------------
def create_slave_context(register_count: int = REGISTER_COUNT):
    """创建单个从站的数据存储区"""
    return ModbusDeviceContext(
        di=ModbusSequentialDataBlock(0, [0] * 10),  # 离散输入
        co=ModbusSequentialDataBlock(0, [0] * 10),  # 线圈
        hr=ModbusSequentialDataBlock(0, [0] * register_count),  # 保持寄存器
        ir=ModbusSequentialDataBlock(0, [0] * 10),  # 输入寄存器
    )

# 创建单从站上下文
slave_context = create_slave_context()
context = ModbusServerContext(devices={1: slave_context}, single=False)

# --------------------------
# 创建设备标识
# --------------------------
identity = ModbusDeviceIdentification()
identity.VendorName = 'IoT-Util'
identity.ProductCode = 'ModbusRS232Sim'
identity.ProductName = 'Modbus RS232 Multi-Sensor Simulator'
identity.ModelName = 'RS232 Model X'
identity.MajorMinorRevision = '1.0'

# --------------------------
# 定时更新从站寄存器值的线程
# --------------------------
def update_registers(server_ref):
    """更新寄存器数据的线程函数"""
    logger.info(f"🔄 [{datetime.now().strftime('%H:%M:%S')}] 开始更新传感器数据...")

    while getattr(server_ref, 'running', True):
        try:
            # 生成6个传感器数据，模拟真实传感器读数
            values = [
                random.randint(1500, 3500),  # 温度传感器 (15.0-35.0°C)
                random.randint(3000, 8000),  # 湿度传感器 (30.0-80.0%)
                random.randint(1000, 2000),  # 压力传感器 (10.0-20.0 bar)
                random.randint(0, 1000),     # 光照传感器 (0-1000 lux)
                random.randint(400, 1200),   # CO2传感器 (400-1200 ppm)
                random.randint(0, 32767)     # 通用数据
            ][:REGISTER_COUNT]

            # 写入保持寄存器地址 0~REGISTER_COUNT-1
            slave_context.setValues(3, 0, values)

            # 记录更新日志
            sensor_names = ['温度', '湿度', '压力', '光照', 'CO2', '通用']
            sensor_data = [f"{sensor_names[i]}:{values[i]/100:.2f}" for i in range(len(values))]
            logger.info(f"📊 从站1 传感器数据: {sensor_data}")

            time.sleep(UPDATE_INTERVAL)
        except Exception as e:
            logger.error(f"❌ 寄存器更新错误: {e}")
            time.sleep(UPDATE_INTERVAL)

# --------------------------
# 启动 RS232 RTU 从站服务
# --------------------------
def start_rs232_server(
    port: str = PORT,
    baudrate: int = BAUDRATE,
    timeout: float = 1.0
):
    """启动RS232服务器"""
    logger.info(f"🚀 启动Modbus RS232服务器...")
    logger.info(f"端口: {port}")
    logger.info(f"波特率: {baudrate}")
    logger.info(f"从站ID: 1")
    logger.info(f"寄存器数量: {REGISTER_COUNT}")

    # 创建服务器引用用于控制停止
    server_ref = type('ServerRef', (), {'running': True})()

    # 启动后台线程更新寄存器
    update_thread = threading.Thread(target=update_registers, args=(server_ref,), daemon=True)
    update_thread.start()

    try:
        # 启动服务器（阻塞运行）
        logger.info(f"✅ RS232服务器已在 {port}@{baudrate} 启动，等待连接...")
        StartSerialServer(
            context=context,
            identity=identity,
            port=port,
            baudrate=baudrate,
            framer=FramerType.RTU,
            timeout=timeout
        )
    except KeyboardInterrupt:
        logger.info("🛑 接收到停止信号")
    except Exception as e:
        logger.error(f"❌ 服务器运行错误: {e}")
    finally:
        # 停止服务器
        server_ref.running = False
        logger.info("🔌 RS232服务器已关闭")

# --------------------------
# 主程序入口
# --------------------------
if __name__ == "__main__":
    start_rs232_server()
